/*	Interactive Gravitational Simulator
	Project: IGS Core
	File: Vector.cpp
	Author: Mike Bantegui <mbante2@gmail.com>, Hofstra University 
	Copyright (C) 2012 - 2013 Mike Bantegui

	This file is part of the IGS software package for simulating
	the N-Body problem in real-time. This was developed as part
	of a departmental honors thesis project at Hofstra University
	in the Spring 2012 semester.

	IGS is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	IGS is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "Vector.h"
#define _USE_MATH_DEFINES
#include <cmath>
#include <iostream>

namespace IGS
{

// Default constructs a Vector. The coordinates are unitialized.
Vector::Vector()
{
}

// Constructs a Vector from the specified coordinates
Vector::Vector(const double x, const double y, const double z) : x(x), y(y), z(z)
{
}

const double &Vector::operator[](const int i) const
{
	return (&x)[i];
}

double &Vector::operator[](const int i)
{
	return (&x)[i];
}

// Zeros the vector
void Vector::Zero()
{
	x = 0;
	y = 0;
	z = 0;
}

// Compound addition of two vectors
Vector &Vector::operator+=(const Vector &rhs)
{
	x += rhs.x;
	y += rhs.y;
	z += rhs.z;
	return *this;
}

// Compound subtraction of two vectors
Vector &Vector::operator-=(const Vector &rhs)
{
	x -= rhs.x;
	y -= rhs.y;
	z -= rhs.z;
	return *this;
}

// Compound division of a vector by a scalar
Vector &Vector::operator/=(const double rhs)
{
	x /= rhs;
	y /= rhs;
	z /= rhs;
	return *this;
}

// Addition of two vectors
Vector operator+(const Vector &lhs, const Vector &rhs)
{
	return Vector(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z);
}

// Difference of two vectors
Vector operator-(const Vector &lhs, const Vector &rhs)
{
	return Vector(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z);
}

// Scalar multiplication of a vector
Vector operator*(const Vector &lhs, const double rhs)
{
	return Vector(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs);
}

// Scalar multiplication of a vector
Vector operator*(const double lhs, const Vector &rhs)
{
	return rhs * lhs;
}

// Scalar division of a vector
Vector operator/(const Vector &lhs, const double rhs)
{
	return lhs * (1.0 / rhs);
}

// Dot product of two vectors
double Dot(const Vector &lhs, const Vector &rhs)
{
	return (lhs.x * rhs.x) + (lhs.y * rhs.y) + (lhs.z * rhs.z);
}

// Euclidean distance between two vectors
double Distance(const Vector &a, const Vector &b)
{
	Vector dr = a - b;
	return sqrt(Dot(dr, dr));
}
// The length of the vector
double Length(const Vector &v)
{
	return sqrt(Dot(v, v));
}

// The squared length of the vector
double LengthSq(const Vector &v)
{
	return Dot(v, v);
}

// The cross product of two vectors
Vector Cross(const Vector &lhs, const Vector &rhs)
{
	return Vector(lhs.y * rhs.z - lhs.z * rhs.y,
				  lhs.z * rhs.x - lhs.x * rhs.z,
				  lhs.x * rhs.y - lhs.y * rhs.x);
}

// Output the vector to an output stream
std::ostream &operator<<(std::ostream &os, const Vector &v)
{
	os << v.x << " " << v.y << " " << v.z;
	return os;
}

// Input a vector from an input stream
std::istream &operator>>(std::istream &is, Vector &v)
{
	is >> v.x >> v.y >> v.z;
	return is;
}

double Random(const double min, const double max)
{
	// + 1 to give number in range [0, 1)
	// this has better properties than [0, 1]
	return min + (max - min) * rand() / (RAND_MAX + 1.0);
}

// Returns a vector uniformly distributed in a size r cube
Vector RandomUniform(const double r)
{
	return Vector(Random(-r, +r),
		Random(-r, +r), Random(-r, +r));
}

Vector RandomInSphere(const double r)
{
	Vector v;
	do
	{
		v.x = Random(-1, +1);
		v.y = Random(-1, +1);
		v.z = Random(-1, +1);
	} while (Dot(v, v) > 1);

	return r * v;
}

// Returns a uniformly distributed on a sphere of radius r
Vector RandomSpherical(const double r)
{
	const double theta = acos(Random(-1, +1));
	const double phi = Random(0, 2 * M_PI);
	return r * Vector(sin(theta) * cos(phi),
		sin(theta) * sin(phi), cos(theta));
}

}
